package hit.core;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import hit.reports.Visitor;

/**
 * The generic name for Objects that can contain Products. This class allows use
 * of the Composite design pattern in the HIT system.
 * */
public abstract class ProductContainer extends HITBase
{
	/**
	 * The {@link ProductGroup}'s that are children of this container.
	 */
	protected List<ProductGroup> productGroups;
	/**
	 * The name of the Container.
	 */
	protected String name;

	/**
	 * Creates a new ProductContainer
	 * @param name
	 */
	public ProductContainer(String name)
	{
		this.name = name;
		productGroups = new ArrayList<ProductGroup>();
	}
	
	/**
	 * Check whether a {@link ProductGroup} can be added to the ProductContainer.
	 * @param productGroup The ProductGroup to add. If the ProductGroup is
	 * invalid, throws an {@link IllegalArgumentException}.
	 * @return whether the ProductGroup can be added to the ProductContainer
	 * */
	public boolean CanAddProductGroup(ProductGroup productGroup) {
		if (productGroup == null || !productGroup.IsValid())
			return false;
		
		//check if any of the children already have that name
		for (ProductGroup child: productGroups) {
			if (child.GetName().equals(productGroup.GetName()))
				return false;
		}
		return true;
	}
	
	/**
	 * Add a {@link ProductGroup} to the ProductContainer.
	 * @param productGroup The ProductGroup to add. If the ProductGroup is
	 * invalid, throws an {@link IllegalArgumentException}.
	 * @throws IllegalArgumentException
	 * */
	public void AddProductGroup(ProductGroup productGroup) throws IllegalArgumentException
	{
		if (!this.CanAddProductGroup(productGroup)) {
			throw new IllegalArgumentException();
		}
		
		productGroups.add(productGroup);
		productGroup.SetParentContainer(this);
	}
	
	public boolean CanEditProductGroup(ProductGroup before, ProductGroup after)
		throws IllegalArgumentException {
		
		if (!before.IsValid() || !after.IsValid())
			return false;
		
		//if (before.GetName() == after.GetName())
			//return false;
		
		//check if any of the children already have that name
		for (ProductGroup child: productGroups) {
			if (child.GetName().equals(after.GetName()) && !child.equals(before))
				return false;
		}
		
		return true;
	}
	
	public String GetName()
	{
		return name;
	}

	public void SetName(String name) throws IllegalArgumentException
	{
		if (name.isEmpty()){
			throw new IllegalArgumentException();
		}
		this.name = name;
	}

	/**
	 * Returns an iterator for the list of {@link ProductGroup}s that
	 * this product container is a parent of.
	 * @return (@link Iterator} over this ProductContainer's ProductGroups.
	 * */
	public Iterator<ProductGroup> GetProductGroupIterator()
	{
		return productGroups.iterator();
	}
	
	/**
	 * Checks whether a given {@link ProductGroup} can be deleted.
	 * @param productGroup the product group in question
	 * @return true if can be deleted. false otherwise.
	 */
	public boolean CanRemoveProductGroup(ProductGroup productGroup){
		HomeInventoryTracker HIT = HomeInventoryTracker.Instance();
		if (productGroups.contains(productGroup) && 
				(HIT.GetItemIndex().GetItemsByProductContainer(productGroup) == null ||
				HIT.GetItemIndex().GetItemsByProductContainer(productGroup).size() == 0)){
			return true;
		}
		else{
			return false;
		}
	}
	/**
	 * Remove a {@link ProductGroup} from the ProductContainer.
	 * @param productGroup The {@link ProductGroup} to be removed. Throws
	 * {@link IllegalArgumentException} if ProductGroup is invalid.
	 * @return true if the ProductGroup was successfully removed. false otherwise.
	 * @throws IllegalArgumentException
	 * */
	public boolean RemoveProductGroup(ProductGroup productGroup) throws IllegalArgumentException
	{
		if (!CanRemoveProductGroup(productGroup)){
			throw new IllegalArgumentException();
		}
		return productGroups.remove(productGroup);
	}
	
	/**
	 * Edits a ProductGroup object instance that is a child of this ProductContainer.
	 * @param before The ProductGroup you wish to edit.
	 * @param after How you wish the ProductContainer to look after the edit.
	 * @throws IllegalArgumentException
	 */
	public void UpdateProductGroup(ProductGroup before, ProductGroup after) 
			throws IllegalArgumentException{
		if (productGroups.contains(before)){
			if (!after.name.isEmpty() && !productGroups.contains(after) && after.IsValid()){
				before.SetName(after.name);
				before.SetParentContainer(after.GetParentContainer());
				before.SetThreeMonth(after.GetThreeMonth());
			}
			else{
				throw new IllegalArgumentException();
			}
		}
		else{
			throw new IllegalArgumentException();
		}
	}
	
	public ProductContainer GetParentContainer() {
		return null;
	}
	
	public StorageUnit GetRootContainer() {
		
		ProductContainer temp = this;
		
		while (!(temp instanceof StorageUnit)) {
			temp = temp.GetParentContainer();
		}
			
		return (StorageUnit)temp;
	}
	
	public boolean IsValid(){
		return true;
	}
	
	//Overrided by StorageUnit and ProductGroup
	public void Accept(Visitor visitor){}
	
	//Overrided by StorageUnit and ProductGroup
	public void AcceptPost(Visitor visitor){}
}
